package com.aliware.tianchi.loadbalance;

import com.aliware.tianchi.common.LogUtils;
import com.aliware.tianchi.common.TianchiRpcContants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author lorne
 * @date 2019/6/26
 * @description
 */
public class CodingApiLoadBalance {


    private boolean debug = true;

    public static final int POOL_SIZE = 1000;

    private boolean selectFlag = false;

    private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private CodingApiLoadBalance(){

    }


    private static CodingApiLoadBalance loadBalance;

    public static CodingApiLoadBalance getInstance(){
        if (loadBalance == null){
            synchronized (CodingApiLoadBalance.class){
                if(loadBalance ==null){
                    loadBalance = new CodingApiLoadBalance();
                }
            }
        }
        return loadBalance;
    }



    public  <T> Invoker<T> selectOne(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
        //获取模块最大线程值
        for(Invoker<T> invoker:invokers) {
            ProviderStatus providerStatus = ProviderStatus.providerStatus(invoker,invocation);
            //为空时询问provider
            if (providerStatus.getMaxThreads() == null) {
                RpcContext.getContext().setAttachment(TianchiRpcContants.ACK_THREADS, invoker.getUrl().getAddress());
                return invoker;
            }
        }
        selectFlag = true;
        //根据平均响应时间的权重来分配
        return getBaseInvokerByAvg(invokers, invocation);
    }

    /**
     * 根据平均响应时间+并发数来控制
     */
    private <T> Invoker<T> getBaseInvokerByAvg(List<Invoker<T>> invokers, Invocation invocation){
        List<WeightNode<Invoker<T>>> weightNodes = new ArrayList<>();
        for(Invoker<T> invoker:invokers) {
            ProviderStatus providerStatus = ProviderStatus.providerStatus(invoker,invocation);
            weightNodes.add(providerStatus.initWeightNode());
        }
        return WeightTools.get(weightNodes);
    }


    public <T> void print(List<Invoker<T>> invokers, Invocation invocation) {
        if(debug&&selectFlag) {
            String content = "time:"+nowTime()+"=>";
            for(Invoker<T> invoker:invokers) {
                ProviderStatus providerStatus = ProviderStatus.providerStatus(invoker,invocation);
                String print = String.format(
                        "key:%s,active:%s,thread:%s,avg:%.2f,c:%.2f,",
                        providerStatus.getAddress(), providerStatus.getActive(),
                        providerStatus.getMaxThreads(), providerStatus.getNowAvg(),
                        providerStatus.getConcurrency());
                content+=print;
            }
            LogUtils.log(content);
        }
    }

    private String nowTime(){
        Date date = new Date();
        return dateFormat.format(date);
    }

}
